#include <msp430.h> 
#include <stdint.h>
#include <string.h>
//From http://blog.csdn.net/xukai871105/article/details/10286599#comments
//Very Good Thanks

#define EEPROM_ADDRESS 0x50


// 软件定时相关操作
#define F_CPU ((double)1200000)
#define delay_us(x) __delay_cycles((long)(F_CPU*(double)x/1000000.0))
#define delay_ms(x) __delay_cycles((long)(F_CPU*(double)x/1000.0))
/*
// 断言相关操作
#define assert_param(expr) ((expr) ? \
  (void)0 : \
  assert_failed((uint8_t *)__FILE__, __LINE__));
void assert_failed(uint8_t* file, uint16_t line);

void assert_failed(uint8_t* file, uint16_t line)
{
  // 输出错误信息
  printf(" file %s on line %d\r\n", file , line);
  while (1)
  {
  }
}
*/

void ucb0_config(void) {
    P3SEL &= ~BIT1;                        // P3.1@UCB0SCL

	P3DIR |= BIT1;
	P3OUT |= BIT1;
	uint8_t i;
	// 输出9个时钟以恢复I2C总线状态
	for (i = 0; i < 9; i++) {
		P3OUT |= BIT1;
		__delay_cycles(8000);
		P3OUT &= ~BIT1;
		__delay_cycles(8000);
	}

	P3SEL |= (BIT0 + BIT1);

	UCB0CTL1 |= UCSWRST;
	UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC;   // I2C主机模式
	UCB0CTL1 |= UCSSEL_2;                  // 选择SMCLK
	UCB0BR0 = 12;                          // fSCL = SMCLK/12 = ~100kHz
	UCB0BR1 = 0;
	UCB0CTL0 &= ~UCSLA10;                  // 7位地址模式
	UCB0I2CSA = EEPROM_ADDRESS;            // EEPROM地址
	UCB0CTL1 &= ~UCSWRST;
}

uint8_t eeprom_writebyte(uint16_t word_addr, uint8_t word_value) {
	while ( UCB0CTL1 & UCTXSTP)
		;      // 不是STOP模式
	UCB0CTL1 |= UCTR;                // 写模式
	UCB0CTL1 |= UCTXSTT;             // 发送启动位

	UCB0TXBUF = (uint8_t) (word_addr >> 8);           // 高8位字节
	// 等待UCTXIFG=1与UCTXSTT=0 同时变化等待一个标志位即可
	while (!(UCB0IFG & UCTXIFG))      //判断是否将数据发送出去了
	{
		if ( UCB0IFG & UCNACKIFG)      // 若无应答 UCNACKIFG=1，如果从机给出无应答，标识出错了
		{
			return 1;
		}
	}
	UCB0TXBUF = (uint8_t) (word_addr & 0xFF);
	while (!(UCB0IFG & UCTXIFG))
		;     // 等待UCTXIFG=1

	UCB0TXBUF = word_value;          // 发送字节内容
	while (!(UCB0IFG & UCTXIFG))
		;     // 等待UCTXIFG=1

	UCB0CTL1 |= UCTXSTP;            //发出STOP模式
	while (UCB0CTL1 & UCTXSTP)
		;       // 等待发送完成

	return 0;
}

uint8_t eeprom_readbyte(uint16_t word_addr, uint8_t * pword_value) {
	UCB0CTL1 |= UCTR;                // 写模式
	UCB0CTL1 |= UCTXSTT;                // 发送启动位和写控制字节

	UCB0TXBUF = (uint8_t) (word_addr >> 8);                // 高8位字节
	// 等待UCTXIFG=1与UCTXSTT=0 同时变化等待一个标志位即可
	while (!(UCB0IFG & UCTXIFG))	//判断是否将数据发送出去了
	{
		if ( UCB0IFG & UCNACKIFG)      // 若无应答 UCNACKIFG=1，如果从机给出无应答，标识出错了
		{
			return 1;
		}
	}
	UCB0TXBUF = (uint8_t) (word_addr & 0xFF);
	while (!(UCB0IFG & UCTXIFG))
		;     // 等待UCTXIFG=1

	UCB0CTL1 &= ~UCTR;     //读模式
	UCB0CTL1 |= UCTXSTT;     // 发送启动位和读控制字节

	while (UCB0CTL1 & UCTXSTT)
		;     // 等待UCTXSTT=0
	// 若无应答 UCNACKIFG = 1
	UCB0CTL1 |= UCTXSTP;	// 先发送停止位

	while (!(UCB0IFG & UCRXIFG))
		;	// 读取字节内容
	*pword_value = UCB0RXBUF;	// 读取BUF寄存器在发送停止位之后

	while ( UCB0CTL1 & UCTXSTP)
		;

	return 0;
}

uint8_t eeprom_writepage(uint16_t word_addr, uint8_t *pword_buf, uint8_t len) {
	while ( UCB0CTL1 & UCTXSTP)
		;
	UCB0CTL1 |= UCTR;                // 写模式
	UCB0CTL1 |= UCTXSTT;             // 发送启动位

	UCB0TXBUF = (uint8_t) (word_addr >> 8);           // 高8位字节
	// 等待UCTXIFG=1与UCTXSTT=0 同时变化等待一个标志位即可
	while (!(UCB0IFG & UCTXIFG))      //判断是否将数据发送出去了
	{
		if ( UCB0IFG & UCNACKIFG)      // 若无应答 UCNACKIFG=1，如果从机给出无应答，标识出错了
		{
			return 1;
		}
	}
	UCB0TXBUF = (uint8_t) (word_addr & 0xFF);
	while (!(UCB0IFG & UCTXIFG))
		;     // 等待UCTXIFG=1
	uint8_t i;
	for (i = 0; i < len; i++) {
		UCB0TXBUF = *pword_buf++;      // 发送寄存器内容
		while (!(UCB0IFG & UCTXIFG))
			;   // 等待UCTXIFG=1
	}

	UCB0CTL1 |= UCTXSTP;
	while (UCB0CTL1 & UCTXSTP)
		;       // 等待发送完成

	return 0;
}

uint8_t eeprom_readpage(uint16_t word_addr, uint8_t *pword_buf, uint8_t len) {
	while ( UCB0CTL1 & UCTXSTP)
		;
	UCB0CTL1 |= UCTR;                // 写模式
	UCB0CTL1 |= UCTXSTT;             // 发送启动位和写控制字节

	UCB0TXBUF = (uint8_t) (word_addr >> 8);           // 高8位字节
	// 等待UCTXIFG=1与UCTXSTT=0 同时变化等待一个标志位即可
	while (!(UCB0IFG & UCTXIFG))      //判断是否将数据发送出去了
	{
		if ( UCB0IFG & UCNACKIFG)      // 若无应答 UCNACKIFG=1，如果从机给出无应答，标识出错了
		{
			return 1;
		}
	}
	UCB0TXBUF = (uint8_t) (word_addr & 0xFF);
	while (!(UCB0IFG & UCTXIFG))
		;     // 等待UCTXIFG=1

	UCB0CTL1 &= ~UCTR;               // 读模式
	UCB0CTL1 |= UCTXSTT;             // 发送启动位和读控制字节

	while (UCB0CTL1 & UCTXSTT)
		;       // 等待UCTXSTT=0
	// 若无应答 UCNACKIFG = 1
	uint8_t i;
	for (i = 0; i < len - 1; i++) {
		while (!(UCB0IFG & UCRXIFG))
			;   // 读取字节内容，不包括最后一个字节内容
		*pword_buf++ = UCB0RXBUF;
	}

	UCB0CTL1 |= UCTXSTP;             // 在接收最后一个字节之前发送停止位

	while (!(UCB0IFG & UCRXIFG))
		;     // 读取最后一个字节内容
	*pword_buf = UCB0RXBUF;

	while ( UCB0CTL1 & UCTXSTP)
		;

	return 0;
}

void eeprom_config()
{

  uint8_t test_byte1 =0x0B;
  uint8_t test_byte2 =0x01;

  /*
    step1 向地址0x00写入某个值，例如0x0B
          然后读出地址0x00结果，判断该值是否为0x0B
  */
  eeprom_writebyte(0x0100, test_byte1);
  delay_ms(20);
  eeprom_readbyte(0x0100,&test_byte2 );
  //assert_param( test_byte1== test_byte2 );

  if( test_byte1== test_byte2 )
  {
    //printf( "Byte Read andByte Write Test Pass\r\n" );
  }

  /*
    step2 以地址0x08作为起始地址，连续写入8个字节数据
          再连续从该起始地址读取8字节内容，比较写入和读出字节内容
          成功的条件为写入和读取字节内容相同
  */
  uint8_t test_buf1[8]= {1,2,3,4,5,6,7,8};
  uint8_t test_buf2[8]= {0,0,0,0,0,0,0,0};

  eeprom_writepage(0x0000 , test_buf1, 8);
  delay_ms(10);
  eeprom_readpage(0x0000 , test_buf2, 8);
  //assert_param( memcmp( test_buf1, test_buf2 ,8) == 0 );

  if(!memcmp( test_buf1, test_buf2 ,8))
  {
    //printf("Page Read andPage Write Test Pass!\r\n");
  }


}

uint8_t pword_value;

void main(void) {
	WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
	ucb0_config();
	eeprom_config();
	while (1) {
		eeprom_readbyte(0x0000, &pword_value);
		__delay_cycles(80000);
	}
}
